package com.web.service.Impl;


import com.web.constant.DataPackageConstants;
import com.web.dao.DeviceDao;
import com.web.exception.BusinessException;
import com.web.observers.DataObservable;
import com.web.observers.RequestResponseObserver;
import com.web.observers.RequestResultObserver;
import com.web.pojo.domain.Device;
import com.web.pojo.domain.DeviceGroup;
import com.web.pojo.dto.*;
import com.web.pojo.vo.CommonResult;
import com.web.pojo.vo.GetSwitchDeviceAttributesVO;
import com.web.pojo.vo.GetSwitchDeviceInfo;
import com.web.pojo.vo.GetSwitchOperatingHours;
import com.web.service.NetworkServer;
import com.web.utils.DownLinkDataUtil;
import com.web.utils.ToolUtil;
import io.netty.channel.Channel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*;

import static com.web.utils.DownLinkDataUtil.show;

/**
 * @Author: wws
 * describe:
 */
@Service
public class NetworkServerImpl implements NetworkServer {

    @Qualifier("getOutQueue")
    @Autowired
    private LinkedBlockingQueue<byte[]> outQueue;

    @Autowired
    public ConcurrentMap<String, Channel> channelMap;

    /**
     * 判断下位机是否回应
     */
    @Qualifier("getGateWayBridgeResponseObservable")
    @Autowired
    private DataObservable gateWayBridgeResponseObservable;

    /**
     * 获取设备回应数据
     */
    @Qualifier("getDeviceResultObservable")
    @Autowired
    private DataObservable deviceResultObservable;

    @Qualifier("getDeviceResponseObservable")
    @Autowired
    private DataObservable deviceResponseObservable;

    @Qualifier("myTaskExecutor")
    @Autowired
    Executor executor;

    @Autowired
    private String gateWayBridgeIp;

    @Autowired
    private DeviceDao deviceDao;



    /**
     * 检查发送数据是否有回应
     * @return 结果
     */
    @Override
    @Async
    public CompletableFuture<Boolean> checkGateWayResponse(RequestResponseObserver requestResponseObserver) {
        gateWayBridgeResponseObservable.addObserver(requestResponseObserver);
        return CompletableFuture.supplyAsync(requestResponseObserver,executor);
    }

    /**
     * 检查发送数据是否有回应
     * @return 结果
     */
    @Async
    public CompletableFuture<Boolean> checkDeviceResponse(RequestResponseObserver requestResponseObserver) {
        deviceResponseObservable.addObserver(requestResponseObserver);
        return CompletableFuture.supplyAsync(requestResponseObserver,executor);
    }

    /**
     * 获取设备应答结果
     * @return 结果
     */
    @Override
    @Async
    public CompletableFuture<byte[]> getDeviceResult(RequestResultObserver requestResultObserver) {
        deviceResultObservable.addObserver(requestResultObserver);
        return CompletableFuture.supplyAsync(requestResultObserver,executor);
    }

    /**
     * 获取设备信息
     * @param device 设备
     * @param fCnt 帧计数器
     * @param FRMPayload 操作命令
     * @param type 操作类型
     * @return 结果
     */
    public CommonResult getSwitch(Device device, byte[] fCnt, byte[] FRMPayload, byte type) {
        byte[] downPayload = DownLinkDataUtil.generateDownPayload(device, FRMPayload);
        byte[] token = ToolUtil.CalAppNonce(2);
        byte[] downData = DownLinkDataUtil.generateDownLinkData(token, DataPackageConstants.STATIC_FREQ, ToolUtil.toByteArray(device.getDeviceAddr()), downPayload);

        RequestResultObserver deviceResponse = new RequestResultObserver(fCnt);
        RequestResponseObserver gatewayResponse = new RequestResponseObserver(token);

        CompletableFuture<Boolean> isDown = downstreamData(downData);
        CompletableFuture<Boolean> gateWayIsResponse = checkGateWayResponse(gatewayResponse);
        CompletableFuture<byte[]> deviceResult = getDeviceResult(deviceResponse);

        try {
            isDown.get(5, TimeUnit.SECONDS);
        } catch (TimeoutException | InterruptedException | ExecutionException e) {
            deviceResultObservable.deleteObserver(deviceResponse);
            gateWayBridgeResponseObservable.deleteObserver(gatewayResponse);
            return CommonResult.error("下位机未连接");
        }

        try {
            gateWayIsResponse.get(5, TimeUnit.SECONDS);
        } catch (TimeoutException | InterruptedException | ExecutionException e) {
            deviceResultObservable.deleteObserver(deviceResponse);
            gateWayBridgeResponseObservable.deleteObserver(gatewayResponse);
            return CommonResult.error("下位机未响应");
        }

        try {
            byte[] result = deviceResult.get(10, TimeUnit.SECONDS);
            switch (type) {
                //查看开关状态
                case DataPackageConstants.SWITCH_LIGHT_STATUS:
                    SetSwitchStateDTO setSwitchStateDTO = new SetSwitchStateDTO(device.getDeviceId(), result);
                    deviceDao.updateDeviceSwitchState(setSwitchStateDTO);
                    return CommonResult.select(setSwitchStateDTO);
                //查看开关控制状态
                case DataPackageConstants.SWITCH_CONTROL_STATUS:
                    SetSwitchControlStateDTO setSwitchControlStateDTO = new SetSwitchControlStateDTO(device.getDeviceId(), result);
                    deviceDao.updateDeviceSwitchControlState(setSwitchControlStateDTO);
                    return CommonResult.select(setSwitchControlStateDTO);
                //查看时间表
                case DataPackageConstants.SWITCH_SCHEDULE:
                    SetSwitchScheduleDTO setSwitchScheduleDTO = new SetSwitchScheduleDTO(device.getDeviceId(),result);
                    deviceDao.replaceDeviceSwitchSchedule(setSwitchScheduleDTO);
                    return CommonResult.select(setSwitchScheduleDTO);
                //查看调光等级
                case DataPackageConstants.SWITCH_DIMMING_LEVEL:
                    // SetSwitchDimmingLevelDTO setSwitchDimmingLevelDTO = new SetSwitchDimmingLevelDTO(device.getDeviceId(), result);
                    // deviceDao.updateDeviceSwitchDimmingLevel(setSwitchDimmingLevelDTO);
                    SetSwitchDimmingLevelDTO setSwitchDimmingLevelDTO = new SetSwitchDimmingLevelDTO(device.getDeviceId(), (byte) 1,(byte) 1);

                    return CommonResult.select(setSwitchDimmingLevelDTO);
                //查看设备状态
                case DataPackageConstants.SWITCH_DEVICE_ATTRIBUTES:
                    GetSwitchDeviceAttributesVO switchDeviceStatusVO = new GetSwitchDeviceAttributesVO(device.getDeviceId(), result);
                    deviceDao.updateDeviceSwitchAttributes(switchDeviceStatusVO);
                    return CommonResult.select(switchDeviceStatusVO);
                //查看设备上报周期
                case DataPackageConstants.SWITCH_REPORTING_PERIOD:
                    SetSwitchReportingPeriodDTO setSwitchReportingPeriodDTO = new SetSwitchReportingPeriodDTO(device.getDeviceId(), result);
                    deviceDao.updateDeviceSwitchReportingPeriod(setSwitchReportingPeriodDTO);
                    return CommonResult.select(setSwitchReportingPeriodDTO);
                //查看设备工作时间
                case DataPackageConstants.SWITCH_OPERATING_HOURS:
                    GetSwitchOperatingHours SwitchOperatingHours = new GetSwitchOperatingHours(device.getDeviceId(), result);
                    deviceDao.updateDeviceSwitchOperatingHours(SwitchOperatingHours);
                    return CommonResult.select(SwitchOperatingHours);
                //查看设备appkey
                case DataPackageConstants.SWITCH_APPKEY:
                    String appKey = getAppKey(result).toUpperCase();
//                    deviceDao.updateDeviceAppkey(device.getDeviceId(),appKey);
                    return CommonResult.select(appKey);
                //获取设备类型、初始化上行通道、版本号
                case DataPackageConstants.SWITCH_GET_INFO:
                    GetSwitchDeviceInfo getSwitchDeviceInfo = new GetSwitchDeviceInfo(device.getDeviceId(), result);
                    deviceDao.updateDeviceSwitchDeviceInfo(getSwitchDeviceInfo);
                    return CommonResult.select(getSwitchDeviceInfo);
                case DataPackageConstants.SWITCH_DELETE_TIME:
                    SetDeviceTimeDTO setDeviceTimeDTO = new SetDeviceTimeDTO(device.getDeviceId(),(byte)1,(byte)1,(byte)1,(byte)1,(byte)1,(byte)1);
                    return CommonResult.select(setDeviceTimeDTO);
                default:
                    return CommonResult.error("error");
            }

        } catch (TimeoutException | InterruptedException | ExecutionException e) {
            return CommonResult.error("error!!!!!!!!!");
        }finally {
            deviceResultObservable.deleteObserver(deviceResponse);
            gateWayBridgeResponseObservable.deleteObserver(gatewayResponse);
        }
    }

    /**
     * 设备分组
     * @param device 设备
     * @param fCnt 帧计数器
     * @param FRMPayload 控制命令
     * @return 结果
     */
    @Override
    public Boolean deviceGrouping(Device device, byte[] fCnt, byte[] FRMPayload) {
        byte[] downPayload = DownLinkDataUtil.generateDownPayload(device, FRMPayload);
        System.out.println("downPayload");
        show(downPayload);
        byte[] token = ToolUtil.CalAppNonce(2);
        byte[] downData = DownLinkDataUtil.DeviceGroupCommands(token, ToolUtil.toByteArray(device.getDeviceBindGateway()), downPayload);
        System.out.println("downData");
        show(downData);
        return sendAndMonitorData(fCnt, token, downData);
    }

    /**
     * 设置设备参数
     * @param device 设备
     * @param fCnt 帧计数器
     * @param FRMPayload 操作数据
     * @return 结果
     */
    public Boolean setSwitch(Device device, byte[] fCnt, byte[] FRMPayload) {
        byte[] downPayload = DownLinkDataUtil.generateDownPayload(device, FRMPayload);
        byte[] token = ToolUtil.CalAppNonce(2);
        byte[] downData = DownLinkDataUtil.generateDownLinkData(token, DataPackageConstants.STATIC_FREQ, ToolUtil.toByteArray(device.getDeviceAddr()), downPayload);
        return sendAndMonitorData(fCnt, token, downData);
    }

    /**
     * 发送数据，并监听回应数据
     * @param fCnt 帧计数器
     * @param token 网关帧计数器
     * @param downData 下行数据
     * @return 结果
     */
    private Boolean sendAndMonitorData(byte[] fCnt, byte[] token, byte[] downData) {
        RequestResponseObserver deviceResponse = new RequestResponseObserver(fCnt);
        RequestResponseObserver gatewayResponse = new RequestResponseObserver(token);
        CompletableFuture<Boolean> isDown = downstreamData(downData);
        CompletableFuture<Boolean> gateWayIsResponse = checkGateWayResponse(gatewayResponse);
        CompletableFuture<Boolean> deviceIsResponse = checkDeviceResponse(deviceResponse);

        try {
            if (!isDown.get(5, TimeUnit.SECONDS)) {
                throw new BusinessException("下位机未连接");
            }
        } catch (TimeoutException | InterruptedException | ExecutionException e) {
            deviceResultObservable.deleteObserver(deviceResponse);
            gateWayBridgeResponseObservable.deleteObserver(gatewayResponse);
        }

        try {
            gateWayIsResponse.get(5, TimeUnit.SECONDS);
        } catch (TimeoutException | InterruptedException | ExecutionException e) {
            deviceResultObservable.deleteObserver(deviceResponse);
            gateWayBridgeResponseObservable.deleteObserver(gatewayResponse);
            throw new BusinessException("下位机未响应");
        }


        try {
            deviceIsResponse.get(10, TimeUnit.SECONDS);
        } catch (TimeoutException | InterruptedException | ExecutionException e) {
            deviceResultObservable.deleteObserver(deviceResponse);
            gateWayBridgeResponseObservable.deleteObserver(gatewayResponse);
            throw new BusinessException("设备未响应");
        }
        return true;
    }

    /**
     * 异步，发送分组数据（无需回应）
     * @param deviceGroup 设备组信息
     * @param deviceList 设备列表
     */
    @Async
    public void deviceGroupAddDevice(DeviceGroup deviceGroup, List<Device> deviceList) {
        for (Device device : deviceList) {
            byte[] FRMPayload = DownLinkDataUtil.deviceGroupAddDevice(ToolUtil.toByteArray(deviceGroup.getDeviceGroupAppkey()), ToolUtil.toByteArray(deviceGroup.getDeviceGroupAddr()), ToolUtil.toByteArray(device.getDeviceEui()));
            byte[] downPayload = DownLinkDataUtil.generateDownPayload(device, FRMPayload);
            byte[] token = ToolUtil.CalAppNonce(2);
            byte[] downData = DownLinkDataUtil.DeviceGroupCommands(token, ToolUtil.toByteArray(device.getDeviceBindGateway()), downPayload);
            downstreamData(downData);
        }
    }


    /**
     * 异步，发送分组数据（无需回应）
     * @param deviceGroup 设备组信息
     * @param deviceList 设备列表
     */
    @Async
    public void deviceGroupSetState(DeviceGroup deviceGroup, List<Device> deviceList) {
        for (Device device : deviceList) {
            byte[] FRMPayload = DownLinkDataUtil.deviceGroupAddDevice(ToolUtil.toByteArray(deviceGroup.getDeviceGroupAppkey()), ToolUtil.toByteArray(deviceGroup.getDeviceGroupAddr()), ToolUtil.toByteArray(device.getDeviceEui()));
            byte[] downPayload = DownLinkDataUtil.generateDownPayload(device, FRMPayload);
            byte[] token = ToolUtil.CalAppNonce(2);
            byte[] downData = DownLinkDataUtil.DeviceGroupCommands(token, ToolUtil.toByteArray(device.getDeviceBindGateway()), downPayload);
            downstreamData(downData);
        }
    }


    String getAppKey(byte[] bytes) {
        return ToolUtil.printHexBinary(Arrays.copyOfRange(bytes, 5, 21));
    }

//    /**
//     * 检查发送数据是否有回应
//     * @return 结果
//     */
//    @Override
//    @Async
//    public CompletableFuture<Boolean> checkResponse(byte[] token) throws InterruptedException {
//        long startingTime = System.currentTimeMillis();
//        long currentTime;
//        Token t = new Token(token);
//        tokenSet.add(t);
//        for (; ; ) {
//            currentTime = System.currentTimeMillis();
//            boolean contains = tokenSet.contains(t);
////            30000
//            if (currentTime - startingTime <= 30000 && !contains) {
//                return CompletableFuture.completedFuture(true);
//            } else if (currentTime - startingTime > 3000) {
//                tokenSet.remove(t);
//                return CompletableFuture.completedFuture(false);
//            }
//        }
//    }

    /**
     * 发送下行数据
     * @return 结果
     */
    @Override
    @Async
    public CompletableFuture<Boolean> downstreamData(byte[] data) {
        try {
            if (channelMap.containsKey(gateWayBridgeIp)) {
                outQueue.put(data);
                return CompletableFuture.completedFuture(true);
            }
            return CompletableFuture.completedFuture(false);
        } catch (InterruptedException e) {
            return CompletableFuture.completedFuture(false);
        }
    }
}
